package com.deploy.util;

import com.deploy.model.ServerInfo;
import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;

/**
 * SCP文件传输工具类
 * 负责将本地文件传输到远程服务器
 */
public class SCPUtil {
    private static final Logger logger = LoggerFactory.getLogger(SCPUtil.class);
    
    /**
     * 通过SCP将本地文件传输到远程服务器
     *
     * @param session SSH会话
     * @param localFilePath 本地文件路径
     * @param remoteFilePath 远程文件路径
     * @return 是否传输成功
     */
    public static boolean transferFile(Session session, String localFilePath, String remoteFilePath) {
        logger.info("开始传输文件: {} -> {}", localFilePath, remoteFilePath);
        
        ChannelExec channel = null;
        FileInputStream fis = null;
        OutputStream out = null;
        
        try {
            // 检查本地文件是否存在
            File localFile = new File(localFilePath);
            if (!localFile.exists() || !localFile.isFile()) {
                logger.error("本地文件不存在: {}", localFilePath);
                return false;
            }
            
            // 创建远程目录（如果不存在）
            String remoteDir;
            int lastSeparatorIndex = remoteFilePath.lastIndexOf('/');
            if (lastSeparatorIndex == -1) {
                // 如果是Windows路径，尝试使用反斜杠
                lastSeparatorIndex = remoteFilePath.lastIndexOf('\\');
                if (lastSeparatorIndex == -1) {
                    // 如果仍然没有找到分隔符，直接使用整个路径
                    remoteDir = remoteFilePath;
                } else {
                    remoteDir = remoteFilePath.substring(0, lastSeparatorIndex);
                }
            } else {
                remoteDir = remoteFilePath.substring(0, lastSeparatorIndex);
            }
            String mkdirCommand = "mkdir -p " + remoteDir;
            SSHUtil.executeCommand(session, mkdirCommand);
            
            // 准备SCP命令
            String command = "scp -p -t " + remoteFilePath;
            channel = (ChannelExec) session.openChannel("exec");
            channel.setCommand(command);
            
            // 获取输入输出流
            out = channel.getOutputStream();
            InputStream in = channel.getInputStream();
            
            // 连接通道
            channel.connect();
            
            // 检查SCP响应
            checkAck(in);
            
            // 发送文件元数据
            command = "C0644 " + localFile.length() + " " + localFile.getName() + "\n";
            out.write(command.getBytes());
            out.flush();
            
            checkAck(in);
            
            // 发送文件内容
            fis = new FileInputStream(localFile);
            byte[] buf = new byte[1024];
            while (true) {
                int len = fis.read(buf, 0, buf.length);
                if (len <= 0) break;
                out.write(buf, 0, len);
            }
            
            // 发送结束标记
            sendEOF(out);
            checkAck(in);
            
            logger.info("文件传输成功: {}", remoteFilePath);
            return true;
            
        } catch (Exception e) {
            logger.error("文件传输失败: {}", e.getMessage(), e);
            return false;
        } finally {
            try {
                if (fis != null) fis.close();
                if (out != null) out.close();
                if (channel != null) channel.disconnect();
            } catch (IOException e) {
                logger.error("关闭资源失败: {}", e.getMessage(), e);
            }
        }
    }
    
    /**
     * 检查SCP响应
     *
     * @param in 输入流
     * @throws IOException 如果发生IO错误
     */
    private static void checkAck(InputStream in) throws IOException {
        int b = in.read();
        if (b == 0) return; // 成功
        if (b == -1) throw new IOException("SCP连接已关闭");
        if (b == 1 || b == 2) {
            StringBuilder sb = new StringBuilder();
            int c;
            do {
                c = in.read();
                sb.append((char) c);
            } while (c != '\n');
            throw new IOException("SCP错误: " + sb.toString());
        }
        throw new IOException("未知SCP错误: " + b);
    }
    
    /**
     * 发送EOF标记
     *
     * @param out 输出流
     * @throws IOException 如果发生IO错误
     */
    private static void sendEOF(OutputStream out) throws IOException {
        byte[] buf = new byte[1];
        buf[0] = 0;
        out.write(buf);
        out.flush();
    }
}
